這裡是「Three.js學習日誌」的第21篇,這篇的內容是要講解Webpack的操作與Webpack config的編寫方法。這系列的文章假設讀者看得懂javascript,並且有Canvas 2D Context的相關知識。
首先我們要知道幾件事:
Webpack必須要在有安裝Node.js的環境下執行Webpack本身需要透過CLI介面來執行,CLI必須要被全域安裝,或者是以devdependency的形式安裝在node_modules中Webpack的CLI會根據專案裡面的webpack.config.js這個文件,來決定要怎麼打包專案webpack.config.js這個檔案其實絕大多數網路上關於
Webpack的問題討論,基本都是在討論webpack.config.js的寫法。
所以我們接下來要簡單介紹一下怎麼樣寫一份webpack.config.js。
webpack.config.js的基本架構想要學習怎麼編寫webpack.config.js,
第一步就是要先記下EOMMP這五個英文字。
E就是我們剛剛提到的Entry
O代表Output,意思是打包出來的檔案要輸出到哪裡。
第一個M代表Mode,意思是當前我們是在哪一種模式底下做打包。
第二個M就是我們剛剛提到的Module
P則代表Plugin,意思是插件。
下面這是一個常見的webpack.config.js檔案,內容看起來的樣子。
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  //這邊是E
  entry: { 
    main:['./src/js/index.js','./src/scss/index.scss'] 
  }, 
  // 這邊是O
  output: {
    filename: 'js/[name].[chunkhash].js', //輸出的檔案名稱格式
    path: resolve(__dirname, 'build'),
  },
   // 這邊是M(1)
  mode: 'development', // 值可以是'development'或是'production'
   // 這邊是M(2)
  module: {
    rules: [
      //...
    ]
  },
  // 這邊是P
  plugins: [
    new HtmlWebpackPlugin({
     template: './index.html'
    })
  ]
}
webpack.config.js的各屬性解釋這邊請容我再解釋一下幾個關於上面這段webpack.config.js的細節。
webpack 官方文件: 點我
entry可以填入專案中用用到的entry files。
我們會把多個entry files構成的集合體稱為Chunk。
這邊的main:['./src/js/index.js','./src/scss/index.scss'] 換句話說就是:
「我們在這個專案中有個叫做main的chunk,它是由index.js和index.scss所構成。
webpack 官方文件: 點我
我們可以在上面的output欄位中看到path: resolve(__dirname, 'build')這樣的寫法。
這一段的resolve和__dirname其實是node.js提供的方法還有變數。
resolve的用途是將一系列路徑或路徑段解析為絕對路徑(詳細請見文件)。
__dirname則是代表webpack.config.js所位於的資料夾名稱(詳細請見文件)。
webpack 官方文件: 點我
mode表示我們當前是在哪一種模式底下做打包。
基本上可以選擇為,development(開發模式) 或 production(生產模式)。
不同的模式採用的打包方式會不同,產生的結果其細節也會不一樣。
例如我們可以選擇只在生產模式下對打包的檔案進行極小化,以減少檔案容量。
webpack 官方文件: 點我
這個部分主要是去定義當webpack偵測到資源的時候,要怎麼去載入或編譯這個資源。
例如假設我們如果要載入.SCSS資源。最簡單的方式就是像下面這樣設置:
module: {
    rules: [
      {
        test: /\.scss$/,
        use: [
          'style-loader',
          'css-loader',
          'sass-loader'
        ]
      },
    ]
},
我們可以注意到這邊的use使用了三個Loader,分別是style-loader、css-loader、sass-loader。 但實際上這三個Loader的執行順序並不是由上往下,而是由下往上。意思也就是說當webpack偵測到SCSS資源的時候:
sass-loader會去把SCSS轉譯為CSS。CSS-loader會去偵測CSS內部的url()和@import轉換成require(),目的是為了讓js可以認知到CSS內部有使用其他資源。style-loader則是把CSS插入DOM中上面的寫法只是一個範例,實際上我們會因為需要應對專案的各種需求去決定module.rules的寫法,而不同類型的資源往往也會有不同的Loader需要使用。(而且就算同樣是要載入SCSS資源,不同的專案需求下可能也出現好幾種寫法)
通常
Loader會是由webpack社群的使用者一起協力開發,不過像這樣的開源插件常常有的毛病就是:更新可能不及時(或是作者人間蒸發),所以通常要選用Loader插件的時候,都要盡量觀察插件NPM頁面/Github Repo的更新狀況和健康程度,要升級版本時也最好都要先確認過Patch note。
webpack 官方文件: 點我
老實說plugin這個屬性並沒有一個一定的解釋。
這個欄位就是用來讓我們導入一些插件,這些插件會在webpack建構打包內容的時候造成影響。
這邊我們可以介紹幾個常用的插件:
webpack.DefinePlugin: 這是一個webpack官方內建的插件,我們可以用它來定義在不同的mode時,產生不同的值作為環境變數。
MiniCssExtractPlugin: 這是一個可以用來把module.rules中偵測到的CSS字串資源抽取出來成為獨立的CSS檔案。
CopyPlugin : 這是一個可以把指定資料夾裡面的內容,複製到另外一個資料夾的插件,通常會使用這個插件,大多是因為有些檔案需要在不被轉譯的情況下使用,所以必須要繞過module.rules>Loader的階段,直接把檔案複製到output的路徑上。
HtmlWebpackPlugin: 這個插件可以透過傳入Entry Chunk的名稱,還有一個html模板的路徑,來把Entry Chunk的內容跟該html模板合併輸出一個html檔案。(意思就是可以把Entry Chunk裡面的js和css塞到目標的html裡面,變成一個有導入樣式表和JS的html檔)。
除了上述這些以外當然還有很多其他的Plugin,狀況跟Loader一樣。
老實說 ~ 筆者其實沒有打算一步一步的去講解到底怎麼樣去寫好一個webpack模板。
這邊可能讓大家失望了QQ
但畢竟競賽的主題不是webpack,而是Three.js,而且如果真的要講完「webpack-template」的開發過程,我預估大概也要講到賽程結束...
因此關於webpack操作的細節,我只打算點到上面為止。
如果真的對如何建立自己的
webpack模板有興趣,我很推薦你去看看這系列的影片
但是由於在接下來幾天中,筆者小弟我都會使用我自己開發的webpack模板:「webpack-template」來進行範例的實作。所以我打算在這邊講解一下我所開發的「webpack-template」的使用方法,還有一部分的機制與原理。
Github Repository: https://github.com/mizok/webpack-template
Github後移步至上述的Repository地址,然後點擊畫面中的綠色"Use this template"按鈕
Repository的頁面,Github將會直接使用筆者開發的webpack-template作為模板來在您的帳號名下建立一個Repository。
Repository後就可以git clone下來。git操作的部分如果不熟悉,可以看看這邊
這個模板主要使用的技術/語言有:
webpack5 (作為主打包程序)typescript
scss
ejs (作為主樣板語言)在Github Repo上面,您其實可以隨時檢閱英文的Readme文件,而若有問題,也歡迎隨時發布ISSUE或投遞PR ~
npm run build : 會呼叫webpack 的 CLI工具執行專案打包npm run dev : 會使用內置的webpack-dev-server來模擬專案的Hosting,用途類似VScode Live Server
npm run deploy : 會使用gh-pages這個插件把專案打包後部署到Github Page

在「webpack-template」中,當使用者按下Enter送出npm run build指令後,首先會先在webpack.config.ts裡面碰到一層判斷。
這層判斷會去根據當前每個./src/pages/底下的EJS檔案的命名方式,來決定要生成哪些Entry Chunks。
例如:
index.ejs 會使程序判斷必須要生成一個被命名為index的Entry Chunks。index.main.ejs 會使程序判斷必須要生成一個被命名為main的Entry Chunks。而當如果專案備程序判定將生成了一個名為"xx"的Entry Chunks時,我們就必須要在:
./src/ts 底下補上一個名為xx.ts的檔案(作為ts entry)./src/scss 底下補上一個名為xx.scss的檔案(作為scss entry)我們之前提到的CopyPlugin,會把在./src/static資料夾底下的所有檔案,一併複製到./dist/static。
通常我們會把一些.pdf或是.obj、.mtl、.gltf等外部3D模型檔案堆放在這個資料夾,以避免它們被Loader 加載到。
其他像是有關於EJS的寫作方法、環境變數的取用之類的Tips,都可以在Repo的README進行查閱~ 這邊就不多做說明。
對於webpack/「webpack-template」的介紹就差不多在這先打住。
明天我們將著手從一個空白的「webpack-template」模板來改造成為我們在接下來的天數會使用到的Three.js Boilerplate !